package com.tuton.javanet.chapter03;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.*;

public class T3EchoServer {
	
	private ServerSocket serverSocket;
	private int port = 8000;
	private ExecutorService executorService;
	private final int sizeOfThreadPool = 4;
	
	private ServerSocket serverScoketForShutdown;
	private int portForShutdown = 8001;
	private boolean isServerClose = false;
	
	public T3EchoServer() throws IOException {
		serverSocket = new ServerSocket(port);
		serverSocket.setSoTimeout(60000);
		
		this.serverScoketForShutdown = new ServerSocket(portForShutdown);
		
		shutdownThread.start();
		
		executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()* 
				sizeOfThreadPool);
System.out.println("Server was started....");
	}
	
	public static void main(String[] args) throws IOException {
		new T3EchoServer().service();
	}
	
	private Thread shutdownThread = new Thread(){
		
		@Override
		public synchronized void start() {
			this.setDaemon(true);
			super.start();
		}

		@Override
		public void run() {
			Socket socketForShutdown = null;
			try{
				while(!isServerClose) {
	 				socketForShutdown = serverScoketForShutdown.accept();
System.out.println("new socketForShutdown : " + 
		socketForShutdown.getInetAddress() + ":" +
		socketForShutdown.getPort());
					BufferedReader br = new BufferedReader(new InputStreamReader(socketForShutdown.getInputStream()));
					
					String command = br.readLine();
					
					if("shutdown".equalsIgnoreCase(command)) {
						long shutdownStartTime = System.currentTimeMillis();
						
						isServerClose = true;
						
						executorService.shutdown();
						
						while(!executorService.isTerminated()) {
							executorService.awaitTermination(30, TimeUnit.SECONDS);
						}
						
						serverSocket.close();
						long shutdownEndTime = System.currentTimeMillis();
						System.out.println("The SERVER wsa shut down It took about " + 
								(shutdownEndTime - shutdownStartTime) + " millis");
						//close the shutdown server and client socket
						socketForShutdown.close();
						serverScoketForShutdown.close();
					} else {
						socketForShutdown.getOutputStream().write("wrong command".getBytes());
						socketForShutdown.close();
					} //#if else
					
				}//#while
			
			} catch(IOException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	};
	
	private void service(){
		Socket socket = null;
		try {
			while(!isServerClose) {
				socket = serverSocket.accept();
				socket.setSoTimeout(60000);
				this.executorService.execute(new Handler(socket));
				
			}
		} catch(SocketTimeoutException t) {
			//no need the handle this exception
		} catch(RejectedExecutionException e) {
			try{
				if(socket != null) {
					socket.close();
				} 
			} catch(IOException IOE) {
				IOE.printStackTrace();
			}
		} catch(SocketException e) {
			if(e.getMessage().indexOf("socket close") != -1) return;
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	
	
	class Handler implements Runnable {

		Socket socket;
		
		public Handler(Socket socket) {
			this.socket = socket;
		}
		
		private BufferedReader getReader(Socket socket) throws IOException {
			InputStream socketIn = socket.getInputStream();
			return new BufferedReader(new InputStreamReader(socketIn));
		}
		
		private PrintWriter getWriter(Socket socket) throws IOException {
			OutputStream socketOut = socket.getOutputStream();
			return new PrintWriter(socketOut,true);
		}
		
		private String echo(String msg) {
			return "echo : " + msg;
		}
		
		public void run() {
			try{
				System.out.println("Got a new Socket connected ; " +
						socket.getInetAddress() + ":" +
						socket.getPort());
				
				BufferedReader br = this.getReader(socket);
				PrintWriter    pw = this.getWriter(socket);
				String  msg = null;
				
				while((msg = br.readLine()) != null) {
					System.out.println("The client type in : " + msg);
				    pw.println(echo(msg));
				    if("bye".equalsIgnoreCase(msg)) {
				    	break;
				    }
				}
			} catch(IOException ex) {
				ex.printStackTrace();
			} finally{
				try{
					if(socket != null) {
						socket.close();
					}
				} catch(IOException ex) {
					ex.printStackTrace();
				}
			}
			
		}
		
	}
}